package com.tmt.jpa.jpaservice.impl;

import com.tmt.annotation.parser.LazyLoadParser;
import com.tmt.helper.ReflectHelper;
import com.tmt.jpa.jpaservice.PropertyLoader;
import org.hibernate.mapping.Collection;
import org.springframework.util.StringUtils;

import javax.persistence.Entity;
import javax.persistence.PersistenceUnitUtil;
import java.util.Arrays;
import java.util.stream.Stream;

/**
 * 属性加载器
 */
public class PropertyLoaderImpl implements PropertyLoader {

    /**
     * 加载实体对象的指定属性。
     * 本方法必须在Spring事务环境下调用，仅对懒加载属性有效
     *
     * @param entity     实体对象。
     * @param properties 要求加载的属性数组，支持*.*格式的属性链。
     * @param <T>
     */
    @Override
    public <T> void loadLazyProperties(T entity, String[] properties, PersistenceUnitUtil unitUtil) {
        if (properties == null || entity == null)
            return;

        Class clazz = LazyLoadParser.getValidClass(entity.getClass());
        if (clazz.getAnnotation(Entity.class) == null)
            return;

        Arrays.stream(properties).forEach(x -> loadLazyProperty(entity, x, unitUtil));
    }

    /**
     * 加载实体属性
     *
     * @param entity
     * @param property 支持*.*格式
     * @param <T>
     */
    private <T> void loadLazyProperty(T entity, String property, PersistenceUnitUtil unitUtil) {
        if (entity == null || StringUtils.isEmpty(property))
            return;

        Class clazz = LazyLoadParser.getValidClass(entity.getClass());
        if (clazz.getAnnotation(Entity.class) == null)
            return;

        String[] propArray = property.split("\\.");

        //加载属性值
        Object fieldValue = null;
        if (!unitUtil.isLoaded(entity, propArray[0])) {
            fieldValue = ReflectHelper.getFieldValue(entity, propArray[0]);
            if (fieldValue == null)
                return;

            ReflectHelper.setFieldValue(entity, propArray[0], fieldValue);
        }

        //删除已经加载了的属性。
        String restProperty = propArray.length > 1 ? property.replaceFirst(propArray[0] + ".", "") : "";
        if (restProperty.trim().isEmpty())
            return;

        //继续加载后续属性。
        if (fieldValue instanceof Collection) {
            Stream.of((Collection) fieldValue).forEach(x -> loadLazyProperty(x, restProperty, unitUtil));
        } else {
            loadLazyProperty(fieldValue, restProperty, unitUtil);
        }
    }
}
